'use strict'

const Service = require('egg').Service
const { Op, literal } = require('sequelize')
const captchapng = require('captchapng')
const crypto = require('crypto')
const { generateRandomCode } = require('../utils')

class SysUserService extends Service {
  /**
   * 登录 post - 权限 open
   * @param {Object} data - 请求参数
   * @property {String} data.username - 用户名
   * @property {String} data.password - 密码
   * @property {String} data.captcha - 验证码
   */
  async login(data) {
    const { ctx, app } = this

    data = Object.assign(
      {
        username: '',
        password: '',
        captcha: ''
      },
      data
    )

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    // 参数校验
    if (!data.username) {
      return ctx.throw(400, { msg: '请输入用户名' })
    }

    if (!data.password) {
      return ctx.throw(400, { msg: '请输入密码' })
    }

    const { register_captcha } = ctx.session
    if (!register_captcha) {
      return ctx.throw(400, { msg: '验证码已失效，请刷新' })
    }
    if (data.captcha != register_captcha) {
      return ctx.throw(400, { msg: '验证码错误' })
    }

    let one = await db.findOne(conditions)
    if (!one) {
      return ctx.throw(400, { msg: '用户名不存在' })
    }

    // 密码校验
    data.password = crypto.createHash('sha256').update(data.password).digest('hex')
    if (data.password !== one.password) {
      return ctx.throw(400, { msg: '密码错误' })
    }

    // 状态校验
    if (one.status !== 1) {
      return ctx.throw(400, { msg: '账号状态异常，请联系管理员' })
    }

    // 通过校验，可以正常登录了

    // jwt数据
    const jwtData = {
      id: one.id,
      username: one.username,
      phone: one.phone,
      email: one.email,
      role: one.role
    }

    // 更新token
    const token = app.jwt.sign(jwtData, app.config.jwt.secret, { expiresIn: app.config.jwt.expires })

    // 更新数据
    const updatedata = {
      token: token,
      login_date: Date.now(),
      login_ip: ctx.request.ip
    }

    await db.update(updatedata, conditions)

    // 查询最新数据
    conditions.attributes = {
      exclude: ['password', 'token', 'register_ip', 'register_date', 'login_ip', 'login_date', 'updated_at', 'created_at'] // 排除字段
    }
    one = await db.findOne(conditions)

    ctx.result({
      data: one,
      token,
      msg: '登录成功'
    })
  }

  /**
   * 刷新token post - 权限 self
   * @param {Object} data - 请求参数
   * @property {String} data.username - 用户名
   */
  async refreshToken(data) {
    const { ctx, app } = this

    if (!data.username) {
      return ctx.throw(400, { msg: 'username 必填' })
    }

    // 权限校验
    ctx.checkAuthority('self', data.username)

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    const one = await db.findOne(conditions)

    // jwt数据
    const jwtData = {
      id: one.id,
      username: one.username,
      phone: one.phone,
      email: one.email,
      role: one.role
    }

    // 更新token
    const token = app.jwt.sign(jwtData, app.config.jwt.secret, { expiresIn: app.config.jwt.expires })

    await db.update({ token: token }, conditions)

    ctx.result({
      token,
      msg: 'token更新成功'
    })
  }

  /**
   * 退出登录 post - 权限 self
   * @param {Object} data - 请求参数
   * @property {String} data.username - 用户名
   */
  async logout(data) {
    const { ctx, app } = this

    if (!data.username) {
      return ctx.throw(400, { msg: 'username 必填' })
    }

    // 权限校验
    ctx.checkAuthority('self', data.username)

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    // 清空token
    await db.update({ token: '' }, conditions)

    ctx.result({
      msg: '退出登录'
    })
  }

  /**
   * 注册 post - 权限 open
   * @param {Object} data - 请求参数
   * @property {String} data.username - 用户名
   * @property {String} data.password - 密码
   * @property {String} data.captcha - 验证码
   * @property {Array} data.role - 角色 默认user，超级管理员默认admin，注册时只能是user/admin，后续可自行调整
   */
  async register(data) {
    const { ctx, app } = this

    data = Object.assign(
      {
        username: '',
        password: '',
        captcha: '',
        role: ['user']
      },
      data
    )

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    // 参数校验
    if (!data.username) {
      return ctx.throw(400, { msg: '请输入用户名' })
    }

    if (!data.password) {
      return ctx.throw(400, { msg: '请输入密码' })
    }

    const { register_captcha } = ctx.session
    if (!register_captcha) {
      return ctx.throw(400, { msg: '验证码已失效，请刷新' })
    }
    if (data.captcha != register_captcha) {
      return ctx.throw(400, { msg: '验证码错误' })
    }

    // 注册时角色只能是user/admin
    if (data.role[0] !== 'user' && data.role[0] !== 'admin') {
      return ctx.throw(400, { msg: 'role 传参错误' })
    }

    // 注册管理员账号时需要校验，不允许重复注册admin
    if (data.role[0] == 'admin') {
      const hadAdmin = await db.findOne({
        where: literal(`JSON_CONTAINS(role, '"admin"')`)
      })
      if (hadAdmin) {
        return ctx.throw(400, { msg: '管理员账号已存在' })
      }
    }

    const one = await db.findOne(conditions)
    if (one) {
      return ctx.throw(400, { msg: '用户名已存在' })
    }

    // 密码加密
    data.password = crypto.createHash('sha256').update(data.password).digest('hex')

    // 账号数据初始化
    data.status = 1
    data.gender = 0
    data.score = 0
    data.my_invite_code = generateRandomCode(10) // 10位随机码
    data.register_ip = ctx.request.ip
    data.register_date = Date.now()

    // 注册用户
    await db.create(data)

    ctx.result({
      msg: '注册成功'
    })
  }

  /**
   * 是否存在admin账号 get - 权限 open
   */
  async hasAdmin() {
    const { ctx, app } = this

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = {
      where: literal(`JSON_CONTAINS(role, '"admin"')`)
    }
    const admin = await db.findOne(conditions)

    ctx.result({
      data: !!admin,
      msg: admin ? '已存在admin账号' : '尚无admin账号'
    })
  }

  /**
   * 获取图形验证码 get - 权限 open
   */
  async getCaptcha() {
    const { ctx, app } = this

    let captchaVal = parseInt(Math.random() * 9000 + 1000)
    let p = new captchapng(80, 30, captchaVal) // width, height, numeric captcha
    p.color(0, 0, 0, 0) // First color: background (red, green, blue, alpha)
    p.color(80, 80, 80, 255) // Second color: paint (red, green, blue, alpha)
    let img = p.getBase64()
    let captcha = 'data:image/png;base64,' + img
    // 返回的类型格式
    ctx.response.type = 'image/png' // base64图形验证码

    // 把验证码保存到session
    ctx.session.register_captcha = captchaVal + '' // 转为字符串
    // 设置session过期时间
    ctx.session.maxAge = 1000 * 60 * 10

    ctx.result({
      data: captcha,
      msg: '验证码获取成功'
    })
  }

  /**
   * 解析token get - 权限 open
   * @param {String} token - token
   */
  async verifyToken(token) {
    const { ctx, app } = this

    // 参数校验
    if (!token) {
      return ctx.throw(400, { msg: 'token 不能为空' })
    }

    const tokenInfo = app.jwt.verify(token, app.config.jwt.secret)

    ctx.result({
      data: tokenInfo,
      msg: 'token解析成功'
    })
  }

  /**
   * 查询 post - 权限 permission
   * @param {Object} data - 请求参数
   * @property {String} data.id - id
   * @property {String} data.username - 用户名
   * @property {String} data.nickname - 昵称
   * @property {String} data.phone - 手机号
   * @property {String} data.email - 邮箱
   * @property {Number} data.pagesize - 每页条数
   * @property {Number} data.pagenum - 页码
   */
  async userList(data) {
    const { ctx, app } = this

    // 权限校验
    ctx.checkAuthority('permission', ['userList'])

    // 参数校验
    let { pagesize = 20, pagenum = 1 } = data
    pagesize = Number(pagesize)
    pagenum = Number(pagenum)

    // 错误参数处理
    if (pagenum < 1) {
      return ctx.throw(400, { msg: 'pagenum不能小于1' })
    }

    // 查询条件处理
    const conditions = {}

    // 分页
    if (pagesize > 0) {
      conditions.limit = pagesize
      conditions.offset = (pagenum - 1) * pagesize
    }

    // 查询语句
    const quary = {}
    if (data.id) {
      quary.id = data.id
    }
    if (data.username) {
      quary.username = { [Op.like]: `%${data.username || ''}%` }
    }
    if (data.nickname) {
      quary.nickname = { [Op.like]: `%${data.nickname || ''}%` }
    }
    if (data.phone) {
      quary.phone = { [Op.like]: `%${data.phone || ''}%` }
    }
    if (data.email) {
      quary.email = { [Op.like]: `%${data.email || ''}%` }
    }
    conditions.where = quary

    // 其他
    conditions.order = [['created_at', 'ASC']] // 排序
    conditions.attributes = {
      exclude: ['password', 'updated_at'] // 排除字段
    }

    // 查询操作
    const { count, rows } = await app.model.SysUser.findAndCountAll(conditions)

    // 总页数
    const pages = pagesize > 0 ? Math.ceil(count / pagesize) : 1

    ctx.result({
      data: rows,
      msg: '获取用户列表成功',
      total: count,
      pagenum,
      pagesize,
      pages
    })
  }

  /**
   * 更新 post - 权限 self
   * @param {Object} data - 请求参数
   * @param {String} data.username - 用户名
   * @param {any} - 更多详见model
   */
  async userUpdate(data) {
    const { ctx, app } = this

    // 参数校验
    data = Object.assign(
      {
        username: '',
        status: 1,
        gender: 0,
        score: 0
      },
      data
    )

    data.status = Number(data.status)
    data.gender = Number(data.gender)
    data.score = Number(data.score)

    if (!data.username) {
      return ctx.throw(400, { msg: 'username 必填' })
    }

    // 权限校验
    ctx.checkAuthority('username', data.username)

    // 数据库连接
    const db = app.model.SysUser

    // 角色校验
    if (data.role?.indexOf('admin') > -1) {
      // 除自己外如果还存在admin则不能再设admin角色
      const admin = await db.findOne({
        where: literal(`JSON_CONTAINS(role, '"admin"')`)
      })
      if (admin && admin.username !== data.username) {
        return ctx.throw(400, { msg: '已存在admin，无法再创建admin角色' })
      }
    }

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    const one = await db.findOne(conditions)
    if (!one) {
      return ctx.throw(400, { msg: '更新项不存在' })
    }

    const res = await db.update(data, conditions)

    ctx.result({
      data: res,
      msg: '更新成功'
    })
  }

  /**
   * 修改状态 post - 权限 permission
   * @param {Object} data - 请求参数
   * @param {String} data.username - 用户名
   * @param {Number} data.status - 状态 0：注销，1：正常，2：封禁
   */
  async changeStatus(data) {
    const { ctx, app } = this

    // 权限校验
    ctx.checkAuthority('permission', ['changeStatus'])

    data = Object.assign(
      {
        username: ''
      },
      data
    )
    data.status = Number(data.status)

    if (!data.username) {
      return ctx.throw(400, { msg: 'username 必填' })
    }

    if (!data.status && data.status !== 0) {
      return ctx.throw(400, { msg: 'status 必填' })
    }

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    const one = await db.findOne(conditions)
    if (!one) {
      return ctx.throw(400, { msg: '用户不存在' })
    }

    const res = await db.update({ status: data.status }, conditions)

    ctx.result({
      data: res,
      msg: '更新成功'
    })
  }

  /**
   * 主动注销 post - 权限 self
   * @param {Object} data - 请求参数
   * @param {String} data.username - 用户名
   */
  async userDeactivate(data) {
    const { ctx, app } = this

    data = Object.assign(
      {
        username: ''
      },
      data
    )

    if (!data.username) {
      return ctx.throw(400, { msg: 'username 必填' })
    }

    // 权限校验
    ctx.checkAuthority('username', data.username)

    // 数据库连接
    const db = app.model.SysUser

    // 查询条件处理
    const conditions = { where: { username: data.username } }

    const one = await db.findOne(conditions)
    if (!one) {
      return ctx.throw(400, { msg: '用户不存在' })
    }

    const res = await db.update({ status: 0 }, conditions)

    ctx.result({
      data: res,
      msg: '注销成功'
    })
  }
}

module.exports = SysUserService
